package cn.airfei.aircore.core.aspect;

import cn.airfei.aircore.core.common.Resp;
import cn.airfei.aircore.core.exception.AppException;
import cn.airfei.aircore.core.extend.InsertLogToMongoDb;
import cn.airfei.aircore.core.extend.ParseHeaderAuthorization;
import cn.airfei.aircore.core.properties.AirCoreProperty;
import cn.airfei.aircore.core.storage.Pojo.LogForMongoDbPojo;
import cn.airfei.aircore.core.utils.CollectionUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: air
 * @create: 2020-11-05 18:12
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class AllControllerLogAspect {
//    @Resource
//    private HttpServletRequest request;

    @Resource
    private AirCoreProperty airCoreProperty;

    @Resource
    private InsertLogToMongoDb insertLogToMongoDb;

    @Resource
    private ParseHeaderAuthorization parseHeaderAuthorization;


    // 定义一个切入点
    @Pointcut("execution(* *..controller.*..*(..))")
    public void log() {

    }

    // 前置通知
    @Before(value = "log()")
    public void before(JoinPoint jp) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String name = jp.getSignature().getName();
        log.info("*****************[" + jp.getSignature().getDeclaringTypeName() + "." + name + "]" + "方法开始执行*****************");
        log.info("[请求IP]:{}", request.getRemoteAddr());
        log.info("[请求URL]:{}", request.getRequestURL());
        log.info("[URL参数]:{}", request.getQueryString());
        log.info("[请求方式]:{}", request.getMethod());

        // 构造参数组集合
        Map<String, Object> argList = new HashMap<>();
        List<Object> bodyList = new ArrayList<>();
        for (Object arg : jp.getArgs()) {
            if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse || arg instanceof BeanPropertyBindingResult) {

            } else if (arg instanceof MultipartFile) {
                // 上传文件
                MultipartFile multipartFile = (MultipartFile) arg;
                bodyList.add(multipartFile.getName() + ":" + multipartFile.getOriginalFilename());
                bodyList.add("fileSize:" + multipartFile.getSize());
            } else {
                if (!CollectionUtil.isEmpty(arg)) {
                    bodyList.add(arg);
                }
            }
        }
        argList.put("paramBody", bodyList);
        // url传参
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap.size() > 0) {
            argList.put("paramPath", parameterMap);
        }
        log.info("[接口请求参数]:{}", JSON.toJSONString(argList));

        // 写入日志到mongoDb
        if (airCoreProperty.getSetLogToMongoDb()) {
            String url = String.valueOf(request.getRequestURL());
            if (!StringUtils.isEmpty(request.getQueryString())) {
                url = url + "?" + request.getQueryString();
            }
            LogForMongoDbPojo logForMongoDbPojo = new LogForMongoDbPojo();
            logForMongoDbPojo.setParams(argList);
            logForMongoDbPojo.setCreateTime(LocalDateTime.now());
            logForMongoDbPojo.setUrl(url);
            logForMongoDbPojo.setTraceId(String.valueOf(request.getAttribute("traceId")));
            logForMongoDbPojo.setIp(request.getRemoteAddr());
            logForMongoDbPojo.setMethod(request.getMethod());
            logForMongoDbPojo.setAuthorization(request.getHeader("Authorization"));

            parseHeaderAuthorization.setLoginInfo(logForMongoDbPojo);
            insertLogToMongoDb.insertLog(logForMongoDbPojo);
        }
    }


    // 后置通知
    @After(value = "log()")
    public void after(JoinPoint jp) {
        String name = jp.getSignature().getName();
    }

    // 返回通知
    @AfterReturning(value = "log()", returning = "result")
    public void afterReturning(JoinPoint jp, Object result) {
        String name = jp.getSignature().getName();
        log.info("[接口返回内容]:{}", JSON.toJSONString(result));
        log.info("*****************[" + jp.getSignature().getDeclaringTypeName() + "." + name + "]" + "方法执行结束*****************");
    }

    // 异常通知
    @AfterThrowing(value = "log()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) {
        String name = jp.getSignature().getName();
        log.error("[" + jp.getSignature().getDeclaringTypeName() + "." + name + "]" + "方法抛异常了，异常是：" + e.getMessage());
    }

    // 环绕通知
    @Around("log()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        // 统计方法执行时间
        long start = System.currentTimeMillis();
        Object result = null;
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            result = jp.proceed();
            long end = System.currentTimeMillis();
            log.info("[方法执行时间]" + ":" + (end - start) + " ms");

            Resp resp = (Resp) result;
            resp.setTraceId(String.valueOf(request.getAttribute("traceId")));

            // 写日志到mongodb
            if (airCoreProperty.getSetLogToMongoDb()) {
                LogForMongoDbPojo logForMongoDbPojo = new LogForMongoDbPojo();
                String url = String.valueOf(request.getRequestURL());
                if (!StringUtils.isEmpty(request.getQueryString())) {
                    url = url + "?" + request.getQueryString();
                }
                logForMongoDbPojo.setResult(resp);
                logForMongoDbPojo.setCreateTime(LocalDateTime.now());
                logForMongoDbPojo.setTraceId(String.valueOf(request.getAttribute("traceId")));
                logForMongoDbPojo.setUrl(url);
                logForMongoDbPojo.setIp(request.getRemoteAddr());
                logForMongoDbPojo.setMethod(request.getMethod());
                logForMongoDbPojo.setExecuteTime((end - start) + "ms");
                logForMongoDbPojo.setAuthorization(request.getHeader("Authorization"));

                parseHeaderAuthorization.setLoginInfo(logForMongoDbPojo);
                insertLogToMongoDb.insertLog(logForMongoDbPojo);
            }
            return resp;
        } catch (AppException e) {
            throw new AppException(e.getCode(), e.getMessage());
        }
    }


}
